package com.qlslylq.ad.sdk.core.loader;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.CountDownTimer;

import com.qlslylq.ad.sdk.application.AdMergeSDK;
import com.qlslylq.ad.sdk.application.AdMergeSDKApp;
import com.qlslylq.ad.sdk.config.AdMergeHolder;
import com.qlslylq.ad.sdk.constant.AppConst;
import com.qlslylq.ad.sdk.core.ad.BaseAd;
import com.qlslylq.ad.sdk.core.listener.AdListener;
import com.qlslylq.ad.sdk.core.listener.BannerAdListener;
import com.qlslylq.ad.sdk.core.listener.FeedAdListener;
import com.qlslylq.ad.sdk.core.listener.FullScreenAdListener;
import com.qlslylq.ad.sdk.core.listener.InterstitialAdListener;
import com.qlslylq.ad.sdk.core.listener.RewardAdListener;
import com.qlslylq.ad.sdk.core.listener.SplashAdListener;
import com.qlslylq.ad.sdk.core.mgr.MediaConfigMgr;
import com.qlslylq.ad.sdk.core.mgr.PolicyCursorMgr;
import com.qlslylq.ad.sdk.core.mgr.PolicyMgr;
import com.qlslylq.ad.sdk.core.mgr.PoolMgr;
import com.qlslylq.ad.sdk.core.rsp.AdError;
import com.qlslylq.ad.sdk.enums.AdEventSign;
import com.qlslylq.ad.sdk.enums.AdPlatformEnum;
import com.qlslylq.ad.sdk.enums.AdType;
import com.qlslylq.ad.sdk.enums.ErrorEnum;
import com.qlslylq.ad.sdk.enums.OrientationEnum;
import com.qlslylq.ad.sdk.model.req.AppBehaviorAddDTO;
import com.qlslylq.ad.sdk.model.req.UserBehaviorAddDTO;
import com.qlslylq.ad.sdk.model.rsp.AdPlaceWebConfigureVO;
import com.qlslylq.ad.sdk.model.rsp.AdPlaceWebVO;
import com.qlslylq.ad.sdk.model.rsp.MediaWebVO;
import com.qlslylq.ad.sdk.net.ResponseObject;
import com.qlslylq.ad.sdk.net.api.ApiServiceImpl;
import com.qlslylq.ad.sdk.rx.subscribers.OnNextOnError;
import com.qlslylq.ad.sdk.util.GsonUtils;
import com.qlslylq.ad.sdk.util.Log;
import com.qlslylq.ad.sdk.util.SPUtils;
import com.qlslylq.ad.sdk.util.ToastUtils;

import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import static com.qlslylq.ad.sdk.enums.OrientationEnum.LANDSCAPE;
import static com.qlslylq.ad.sdk.enums.OrientationEnum.PORTRAIT;

/**
 * 广告加载器基类<br/>
 */
public abstract class AdLoader<T extends AdListener> {

    //组内计时器tick
    private static final long WAIT_AD_TICK = 200;

    //递归默认最大等待时长(ms)(同时考虑开屏的递归)
    private static final int RECURSION_MAX_TIME = 3000;

    //递归默认最大次数(同时考虑开屏的递归)
    private static final int RECURSION_MAX_LIMIT = 10;

    //环境
    protected Activity context;

    //展示方向
    protected OrientationEnum orientation;

    //聚合广告位ID
    protected long posId;

    //广告事件
    private static SplashAdListener splashAdListener;
    private static RewardAdListener rewardAdListener;
    private static FullScreenAdListener fullScreenAdListener;
    private static InterstitialAdListener interstitialAdListener;
    private static BannerAdListener bannerAdListener;
    private static FeedAdListener feedAdListener;

    //广告类型
    protected AdType adType;

    //策略缓存key
    private String policyKey;

    //策略游标缓存key
    private String cursorKey;

    //组内计时器
    private WaitAdTimer waitAdTimer;

    //组外计时器
    private WaitAdMaxTimer waitAdMaxTimer;

    //组外计时器是否耗尽
    private boolean isMaxTimerFinish = false;

    //是否正在递归
    private boolean isRecursioning = false;

    //是否已开启递归计时器
    private boolean hasRecursionTimerStart = false;

    //递归次数
    private AtomicInteger recursionIndex = new AtomicInteger(0);

    //tryAgain子策略
    private boolean tryAgain = false;

    //首次执行OnAdShowSafety
    private static boolean firstOnAdShowSafety = true;

    public AdLoader(Activity context, long posId, T adListener) {
        this.context = context;
        this.posId = posId;
        if (posId <= 0) {
            throw new IllegalArgumentException("无效的聚合广告位ID");
        }
        if (adListener == null) {
            throw new IllegalArgumentException("事件参数不能为null");
        }
        if (this instanceof SplashAdLoader) {
            adType = AdType.SPLASH;
            policyKey = SPUtils.JO_SPLASH_POLICY;
            cursorKey = SPUtils.KEY_SPLASH_CURSOR;
            splashAdListener = (SplashAdListener) adListener;
        } else if (this instanceof RewardAdLoader) {
            adType = AdType.REWARDED;
            policyKey = SPUtils.JO_REWARD_POLICY;
            cursorKey = SPUtils.KEY_REWARD_CURSOR;
            rewardAdListener = (RewardAdListener) adListener;
        } else if (this instanceof FullScreenAdLoader) {
            adType = AdType.FULLSCREEN;
            policyKey = SPUtils.JO_FULL_SCREEN_POLICY;
            cursorKey = SPUtils.KEY_FULL_SCREEN_CURSOR;
            fullScreenAdListener = (FullScreenAdListener) adListener;
        } else if (this instanceof InterstitialAdLoader) {
            adType = AdType.INTERSTITIAL;
            policyKey = SPUtils.JO_INTERSTITIAL_POLICY;
            cursorKey = SPUtils.KEY_INTERSTITIAL_CURSOR;
            interstitialAdListener = (InterstitialAdListener) adListener;
        } else if (this instanceof BannerAdLoader) {
            adType = AdType.BANNER;
            policyKey = SPUtils.JO_BANNER_POLICY;
            cursorKey = SPUtils.KEY_BANNER_CURSOR;
            bannerAdListener = (BannerAdListener) adListener;
        } else if (this instanceof FeedAdLoader) {
            adType = AdType.FEED;
            policyKey = SPUtils.JO_FEED_POLICY;
            cursorKey = SPUtils.KEY_FEED_CURSOR;
            feedAdListener = (FeedAdListener) adListener;
        }
        orientation = (context.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ? LANDSCAPE : PORTRAIT);
    }

    /**
     * 加载<br/>
     */
    public void load() {
        Log.dp("load");
        if (AdMergeSDK.getMediaWebVO() == null) {//所有平台将不可用
            return;
        }

        PolicyCursorMgr.getInstance().resetCursor(cursorKey);
        PoolMgr.getInstance().output(adType);
        PolicyCursorMgr.getInstance().output(adType, cursorKey);
        int size = PoolMgr.getInstance().size(adType);
        if (size > 0) {//有缓存
            preload();
        } else {//缓存已耗尽，实时加载下一组(同onFinish)
            preloadNoFilter();
        }
    }

    /**
     * 预加载第一组<br/>
     */
    private void preload() {
        int size = PoolMgr.getInstance().size(adType);
        Log.dp(String.format("广告缓存池容量为%s，预加载第一组[%s]：", size, adType.getId()) + (size <= 1));
        if (size <= 1) {
            getPlaceAdListForPreload(true);
        }
    }

    /**
     * 预加载第一组<br/>
     */
    private void preloadNoFilter() {
        int size = PoolMgr.getInstance().size(adType);
        Log.dp(String.format("广告缓存池容量为%s，预加载第一组[%s]：", size, adType.getId()) + (size <= 1));
        if (size <= 1) {
            getPlaceAdListForPreload(false);
        }
    }

    /**
     * 展示<br/>
     */
    public void show() {
        loadAndShow();
    }

    /**
     * 加载并展示<br/>
     */
    public void loadAndShow() {
        Log.dp("loadAndShow");
        if (AdMergeSDK.getMediaWebVO() == null) {//所有平台将不可用
            cancelAllTimer();
            if (adType == AdType.SPLASH) {
                onAdShowErrorSafety(ErrorEnum.ERR_SHOW_SPLASH_NEED_DELAY.rsp(AdPlatformEnum.PLATFORM_DY, adType), AdPlatformEnum.PLATFORM_DY, false);
            } else {
                onAdShowErrorSafety(ErrorEnum.ERR_INIT_DY_SDK.rsp(AdPlatformEnum.PLATFORM_DY, adType), AdPlatformEnum.PLATFORM_DY, false);
            }
            return;
        }

        PolicyCursorMgr.getInstance().resetCursor(cursorKey);
        PoolMgr.getInstance().output(adType);
        PolicyCursorMgr.getInstance().output(adType, cursorKey);
        int size = PoolMgr.getInstance().size(adType);
        if (size > 0) {//有缓存，取首
            BaseAd ad = PoolMgr.getInstance().first(adType);
            AdPlatformEnum platform = ad.getConfig().getPlatform();
            if (!AdMergeHolder.isInitSuccess(platform)) {//平台不可用时，开始递归
                if (waitAdTimer != null) {
                    waitAdTimer.cancel();
                }
                Log.ep(platform.getId() + "平台不可用，自动展示下一个广告...");
                isRecursioning = true;
                pop(ad);
                startWaitAdMaxTimer();
                loadAndShow(recursionIndex);
                return;
            }

            cancelAllTimer();
            addPlatformView(platform);
            Log.dp(String.format("当前广告展示中[%s]：", adType.getId()) + ad);
            pop(ad);
            showSafety(ad);
        } else {//缓存已耗尽，实时加载下一组(同onFinish)
            int cursor = PolicyCursorMgr.getInstance().getCursor(cursorKey);
            AdPlaceWebVO policy = PolicyMgr.getInstance().getPolicy(policyKey);
            int policyConfigSize = PolicyMgr.getInstance().getPolicyGroupSize(policy);
            Log.dp(String.format("当前策略组数量[%s]：size = %s，cursor = %s", adType.getId(), policyConfigSize, 0));
            if (policy != null) {//策略存在，请求第一组
                if (policyConfigSize == 0) {
                    onAdShowErrorSafety(ErrorEnum.ERR_NO_AD.rsp(AdPlatformEnum.PLATFORM_DY, adType), AdPlatformEnum.PLATFORM_DY, false);
                    return;
                }
                PolicyCursorMgr.getInstance().setCursor(cursorKey, ++cursor);
                List<AdPlaceWebConfigureVO> list = PolicyMgr.getInstance().getPolicyGroup(policyKey, cursor);
                //并行请求
                long waitAdFuture = (long) policy.getParallelRequestTime() * 1000;
                long waitAdMaxFuture = (long) policy.getRequestTime() * 1000;
                startWaitAdTimer(waitAdFuture);
                startWaitAdMaxTimer(waitAdMaxFuture);

                list.forEach(item -> {
                    if (AppConst.openSecretLog) {
                        Log.dp(String.format("当前广告加载中[%s]：", adType.getId()) + GsonUtils.gsonString(item));
                    }
                    loadSafety(item);

                    ApiServiceImpl.addTrack(UserBehaviorAddDTO.build(item, AdEventSign.AD_REQUEST, posId));
                });
            } else {//策略不存在或策略耗尽，游标转置，刷新策略
                getPlaceAdList(true);
            }
        }
    }

    /**
     * 加载并展示<br/>
     * 用于递归的发起<br/>
     * 组外计时器在第一次递归发起时重启，过程中不重启<br/>
     */
    protected void loadAndShow(AtomicInteger i) {
        i.incrementAndGet();
        Log.dp(String.format("loadAndShow：%s", i));
        PolicyCursorMgr.getInstance().resetCursor(cursorKey);
        PoolMgr.getInstance().output(adType);
        PolicyCursorMgr.getInstance().output(adType, cursorKey);
        int size = PoolMgr.getInstance().size(adType);
        if ((isRecursioning && isMaxTimerFinish) || i.get() > RECURSION_MAX_LIMIT) {//递归结束，实际一般在组内计时器中结束
            Log.ep("递归结束");
            cancelAllTimer();
            onAdShowErrorSafety(ErrorEnum.ERR_NO_AD.rsp(AdPlatformEnum.PLATFORM_DY, adType), AdPlatformEnum.PLATFORM_DY, false);
            return;
        }
        if (size > 0) {//有缓存，取首
            BaseAd ad = PoolMgr.getInstance().first(adType);
            AdPlatformEnum platform = ad.getConfig().getPlatform();
            if (!AdMergeHolder.isInitSuccess(platform)) {//平台不可用时，开始递归
                if (waitAdTimer != null) {
                    waitAdTimer.cancel();
                }
                Log.ep(platform.getId() + "平台不可用，自动展示下一个广告...");
                isRecursioning = true;
                pop(ad);
                loadAndShow(i);
                return;
            }

            cancelAllTimer();
            addPlatformView(platform);
            Log.dp(String.format("当前广告展示中[%s]：", adType.getId()) + ad);
            pop(ad);
            showSafety(ad);
        } else {//缓存已耗尽，实时加载第一组(同onFinish)
            int cursor = PolicyCursorMgr.getInstance().getCursor(cursorKey);
            AdPlaceWebVO policy = PolicyMgr.getInstance().getPolicy(policyKey);
            int policyConfigSize = PolicyMgr.getInstance().getPolicyGroupSize(policy);
            Log.dp(String.format("当前策略组数量[%s]：size = %s，cursor = %s", adType.getId(), policyConfigSize, cursor));
            if (policy != null) {//策略存在，请求第一组
                if (policyConfigSize == 0) {
                    onAdShowErrorSafety(ErrorEnum.ERR_NO_AD.rsp(AdPlatformEnum.PLATFORM_DY, adType), AdPlatformEnum.PLATFORM_DY, false);
                    return;
                }
                PolicyCursorMgr.getInstance().setCursor(cursorKey, ++cursor);
                List<AdPlaceWebConfigureVO> list = PolicyMgr.getInstance().getPolicyGroup(policyKey, cursor);
                //并行请求
                long waitAdFuture = (long) policy.getParallelRequestTime() * 1000;
                Log.ep(String.format("正在开启组内计时器：future = %s，tick = %s", waitAdFuture, WAIT_AD_TICK));
                if (waitAdTimer == null) {
                    waitAdTimer = new WaitAdTimer(waitAdFuture, WAIT_AD_TICK);
                    waitAdTimer.start();
                } else {
                    waitAdTimer.cancel();
                    waitAdTimer.start();
                }
                list.forEach(item -> {
                    if (AppConst.openSecretLog) {
                        Log.dp(String.format("当前广告加载中[%s]：", adType.getId()) + GsonUtils.gsonString(item));
                    }
                    loadSafety(item);

                    ApiServiceImpl.addTrack(UserBehaviorAddDTO.build(item, AdEventSign.AD_REQUEST, posId));
                });
            } else {//策略不存在或策略耗尽，游标转置，刷新策略
                getPlaceAdList(true);
            }
        }
    }

    /**
     * 安全加载<br/>
     */
    private void loadSafety(AdPlaceWebConfigureVO config) {
        try {
            load(config);
        } catch (Exception e) {
            e.printStackTrace();
            Log.ep("loadSafetyError：" + e);
            getAdListener().onAdLoadError(ErrorEnum.ERR_LOAD.rsp(config.getPlatform(), adType));

            ApiServiceImpl.addTrack(UserBehaviorAddDTO.build(config, AdEventSign.AD_RETURN_FAIL, posId));
        }
    }

    /**
     * 安全展示<br/>
     */
    private void showSafety(BaseAd ad) {
        try {
            if (!context.isFinishing()) {
                show(ad);
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.ep("tryAgain：showSafetyError：" + e);
            if (adType == AdType.SPLASH || adType == AdType.BANNER || adType == AdType.INTERSTITIAL || adType == AdType.REWARDED || adType == AdType.FULLSCREEN || adType == AdType.FEED) {
                tryAgain(ad.getConfig().getPlatform());
            } else {
                onAdShowErrorSafety(ErrorEnum.ERR_SHOW.rsp(ad.getConfig().getPlatform(), adType), ad.getConfig().getPlatform(), false);

                ApiServiceImpl.addTrack(UserBehaviorAddDTO.build(ad.getConfig(), AdEventSign.AD_RETURN_FAIL, posId));
            }
        }
    }

    /**
     * 展示成功回调拦截<br/>
     */
    protected void onAdShowSafety() {
        if (context.isFinishing()) {
            return;
        }

        tryAgain = false;
        getAdListener().onAdShow();
        preload();

        if (firstOnAdShowSafety) {
            firstOnAdShowSafety = false;
            ApiServiceImpl.getMediaConfigList(AdMergeSDK.getConfig().getAppId(), new OnNextOnError<ResponseObject<MediaWebVO>>() {
                public void onNext(ResponseObject<MediaWebVO> rsp) {
                    AdMergeSDK.setMediaWebVO(rsp.getData());
                    MediaConfigMgr.getInstance().setMediaConfig(rsp.getData());
                    ApiServiceImpl.addTrack(AppBehaviorAddDTO.build(AdEventSign.APP_OPEN));
                }

                public void onError(ResponseObject rsp) {
                    Log.ep(String.format("获取配置信息失败：%s", GsonUtils.gsonString(rsp)));
                }
            });
        }
    }

    /**
     * 展示失败回调拦截<br/>
     */
    protected void onAdShowErrorSafety(AdError error, AdPlatformEnum platform, boolean runTryAgain) {
        if (context.isFinishing()) {
            return;
        }

        if (runTryAgain) {
            tryAgain(platform);
        } else {
            getAdListener().onAdShowError(error);
            preload();
        }
    }

    /**
     * 展示失败重试<br/>
     * 若发生没有重置-->try again为true-->show error[即下次try again失败],try again为false-->即下下次try again会正常开启运转<br/>
     */
    private void tryAgain(AdPlatformEnum platform) {
        Log.ep(String.format("tryAgain：platform=%s，tryAgain=%s", platform, tryAgain));
        if (!tryAgain) {
            Set<AdType> tryAgainAdTypes = AdMergeSDK.getConfig().getTryAgainAdTypes();
            if (tryAgainAdTypes == null || tryAgainAdTypes.contains(adType)) {
                tryAgain = true;
                loadAndShow();
            } else {
                tryAgain = false;
                onAdShowErrorSafety(ErrorEnum.ERR_SHOW_FAILD.rsp(platform, adType), platform, false);
            }
        } else {
            tryAgain = false;
            onAdShowErrorSafety(ErrorEnum.ERR_SHOW_FAILD.rsp(platform, adType), platform, false);
        }
    }

    /**
     * 重启组内计时器<br/>
     */
    private void startWaitAdTimer(long waitAdFuture) {
        Log.ep(String.format("正在开启组内计时器：future = %s，tick = %s", waitAdFuture, WAIT_AD_TICK));
        if (waitAdTimer == null) {
            waitAdTimer = new WaitAdTimer(waitAdFuture, WAIT_AD_TICK);
            waitAdTimer.start();
        } else {
            waitAdTimer.cancel();
            waitAdTimer.start();
        }
    }

    /**
     * 重启组外计时器<br/>
     */
    private void startWaitAdMaxTimer(long waitAdMaxFuture) {
        Log.ep(String.format("正在开启组外计时器：future = %s，tick = %s", waitAdMaxFuture, waitAdMaxFuture));
        if (waitAdMaxTimer == null) {
            waitAdMaxTimer = new WaitAdMaxTimer(waitAdMaxFuture, waitAdMaxFuture);
            waitAdMaxTimer.start();
        } else {
            waitAdMaxTimer.cancel();
            waitAdMaxTimer.start();
        }
    }

    /**
     * 重启组外计时器<br/>
     * 仅限第一次递归发起时使用<br/>
     */
    private void startWaitAdMaxTimer() {
        if (!hasRecursionTimerStart) {
            hasRecursionTimerStart = true;
            AdPlaceWebVO policy = PolicyMgr.getInstance().getPolicy(policyKey);
            if (policy != null) {
                long waitAdMaxFuture = (long) policy.getRequestTime() * 1000;
                startWaitAdMaxTimer(waitAdMaxFuture);
            } else {
                startWaitAdMaxTimer(RECURSION_MAX_TIME);
            }
        }
    }

    /**
     * 组内计时器<br/>
     */
    private class WaitAdTimer extends CountDownTimer {

        private long millisInFuture;

        public WaitAdTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
            this.millisInFuture = millisInFuture;
        }

        public void onTick(long millisUntilFinished) {
            if (isMaxTimerFinish) {
                cancelAllTimer();
                onAdShowErrorSafety(ErrorEnum.ERR_NO_AD.rsp(AdPlatformEnum.PLATFORM_DY, adType), AdPlatformEnum.PLATFORM_DY, false);
                return;
            }

            if (!PoolMgr.getInstance().isEmpty(adType)) {
                Log.dp(String.format("组内计时器[未超时]：cursor = %s", PolicyCursorMgr.getInstance().getCursor(cursorKey)));
                cancel();

                if (!context.isFinishing()) {
                    BaseAd ad = PoolMgr.getInstance().first(adType);
                    AdPlatformEnum platform = ad.getConfig().getPlatform();
                    if (!AdMergeHolder.isInitSuccess(platform)) {//平台不可用时，开始递归
                        Log.ep(platform.getId() + "平台不可用，自动展示下一个广告...");
                        isRecursioning = true;
                        pop(ad);
                        startWaitAdMaxTimer();
                        loadAndShow(recursionIndex);
                        return;
                    }

                    cancelAllTimer();
                    addPlatformView(platform);
                    Log.dp(String.format("当前广告展示中[%s]：", adType.getId()) + ad);
                    pop(ad);
                    showSafety(ad);
                } else {
                    waitAdMaxTimer.cancel();
                    isMaxTimerFinish = false;
                    isRecursioning = false;
                }
            }
        }

        public void onFinish() {//同缓存已耗尽，实时加载下一组
            if (isMaxTimerFinish) {
                cancelAllTimer();
                onAdShowErrorSafety(ErrorEnum.ERR_NO_AD.rsp(AdPlatformEnum.PLATFORM_DY, adType), AdPlatformEnum.PLATFORM_DY, false);
                return;
            }
            int cursor = PolicyCursorMgr.getInstance().getCursor(cursorKey);
            Log.ep(String.format("组内计时器[超时]：cursor = %s，millisInFuture = %s", cursor, millisInFuture));
            AdPlaceWebVO policy = PolicyMgr.getInstance().getPolicy(policyKey);
            int policyConfigSize = PolicyMgr.getInstance().getPolicyGroupSize(policy);
            Log.dp(String.format("当前策略组数量[%s]：size = %s，cursor = %s", adType.getId(), policyConfigSize, cursor));
            if (cursor < policyConfigSize - 1 && policy != null) {//策略存在，请求下一组
                PolicyCursorMgr.getInstance().setCursor(cursorKey, ++cursor);
                List<AdPlaceWebConfigureVO> list = PolicyMgr.getInstance().getPolicyGroup(policyKey, cursor);
                //并行请求   待开启计时器  最先加载成功的外抛
                if (waitAdTimer == null) {
                    waitAdTimer = new WaitAdTimer((long) policy.getParallelRequestTime() * 1000, 200);
                    startTimer();
                } else {
                    waitAdTimer.cancel();
                    startTimer();
                }
                list.forEach(item -> {
                    if (AppConst.openSecretLog) {
                        Log.dp(String.format("当前广告加载中[%s]：", adType.getId()) + GsonUtils.gsonString(item));
                    }
                    loadSafety(item);

                    ApiServiceImpl.addTrack(UserBehaviorAddDTO.build(item, AdEventSign.AD_REQUEST, posId));
                });
            } else {//策略不存在或策略耗尽，游标转置，刷新策略
                cancelAllTimer();
                onAdShowErrorSafety(ErrorEnum.ERR_NO_AD.rsp(AdPlatformEnum.PLATFORM_DY, adType), AdPlatformEnum.PLATFORM_DY, false);
            }
        }

        private void startTimer() {
            waitAdTimer.start();
        }
    }

    /**
     * 组外计时器<br/>
     */
    private class WaitAdMaxTimer extends CountDownTimer {

        private long millisInFuture;

        public WaitAdMaxTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
            this.millisInFuture = millisInFuture;
        }

        public void onTick(long millisUntilFinished) {

        }

        public void onFinish() {
            Log.ep(String.format("组外计时器[超时]：cursor = %s，millisInFuture = %s", PolicyCursorMgr.getInstance().getCursor(cursorKey), millisInFuture));
            isMaxTimerFinish = true;
        }
    }

    /**
     * 外抛前关闭内外计时器<br/>
     */
    private void cancelAllTimer() {
        if (waitAdTimer != null) {
            waitAdTimer.cancel();
        }
        if (waitAdMaxTimer != null) {
            waitAdMaxTimer.cancel();
        }
        isMaxTimerFinish = false;
        isRecursioning = false;
        hasRecursionTimerStart = false;
        recursionIndex.set(0);
    }

    /**
     * 更新策略<br/>
     * 更新策略前需要先重置策略，以应对网络失败情况<br/>
     */
    private void getPlaceAdList(boolean needShow) {
        Log.dp(String.format("准备请求更新广告位策略：needShow = %s", needShow));
        PolicyCursorMgr.getInstance().resetCursor(cursorKey);
        PolicyMgr.getInstance().resetPolicy(policyKey);
        ApiServiceImpl.getPlaceAdList(posId, new OnNextOnError<ResponseObject<AdPlaceWebVO>>() {
            public void onNext(ResponseObject<AdPlaceWebVO> rsp) {
                AdPlaceWebVO jo = rsp.getData();
                if (jo == null) {
                    ResponseObject obj = new ResponseObject();
                    obj.setCode(ErrorEnum.ERR_REMOTE_NO_POS_AD_CONFIGS.getId());
                    obj.setMessage(ErrorEnum.ERR_REMOTE_NO_POS_AD_CONFIGS.getName());
                    onError(obj);
                    return;
                }
                //策略数据加工
                jo.process();
                //策略覆盖，不追加
                PolicyMgr.getInstance().setPolicy(policyKey, jo);
                if (needShow) {
                    if (isRecursioning) {
                        loadAndShow(recursionIndex);
                    } else {
                        loadAndShow();
                    }
                } else {
                    preload();
                }
            }

            public void onError(ResponseObject rsp) {
                Log.ep(String.format("更新策略失败[%s]", adType.getId()));
                if (needShow) {
                    getAdListener().onAdLoadError(AdError.build(AdPlatformEnum.PLATFORM_DY, adType, rsp.getCode(), "更新策略失败：" + rsp.getMessage()));
                }
            }
        });
    }

    /**
     * 更新策略<br/>
     * 更新策略前需要先重置策略，以应对网络失败情况<br/>
     */
    private void getPlaceAdListForPreload(boolean filter) {
        Log.dp(String.format("准备请求更新广告位策略：needShow = %s", false));
        PolicyCursorMgr.getInstance().resetCursor(cursorKey);
        PolicyMgr.getInstance().resetPolicy(policyKey);
        ApiServiceImpl.getPlaceAdList(posId, new OnNextOnError<ResponseObject<AdPlaceWebVO>>() {
            public void onNext(ResponseObject<AdPlaceWebVO> rsp) {
                AdPlaceWebVO policy = rsp.getData();
                if (policy == null) {
                    ResponseObject obj = new ResponseObject();
                    obj.setCode(ErrorEnum.ERR_REMOTE_NO_POS_AD_CONFIGS.getId());
                    obj.setMessage(ErrorEnum.ERR_REMOTE_NO_POS_AD_CONFIGS.getName());
                    onError(obj);
                    return;
                }
                //策略数据加工
                policy.process();
                //策略覆盖，不追加
                PolicyMgr.getInstance().setPolicy(policyKey, policy);
                //预加载第一组
                List<AdPlaceWebConfigureVO> list = PolicyMgr.getInstance().getPolicyGroup(policyKey, 0);
                int policyConfigSize = PolicyMgr.getInstance().getPolicyGroupSize(policy);
                if (policyConfigSize == 0) {
                    ResponseObject obj = new ResponseObject();
                    obj.setCode(ErrorEnum.ERR_REMOTE_NO_POS_AD_CONFIGS.getId());
                    obj.setMessage(ErrorEnum.ERR_REMOTE_NO_POS_AD_CONFIGS.getName());
                    onError(obj);
                    return;
                }
                //并行请求
                if (filter) {
                    if (adType != AdType.SPLASH && adType != AdType.BANNER) {
                        list.forEach(item -> {
                            if (AppConst.openSecretLog) {
                                Log.dp(String.format("当前广告加载中[%s]：", adType.getId()) + GsonUtils.gsonString(item));
                            }
                            loadSafety(item);

                            ApiServiceImpl.addTrack(UserBehaviorAddDTO.build(item, AdEventSign.AD_REQUEST, posId));
                        });
                    }
                } else {
                    list.forEach(item -> {
                        if (AppConst.openSecretLog) {
                            Log.dp(String.format("当前广告加载中[%s]：", adType.getId()) + GsonUtils.gsonString(item));
                        }
                        loadSafety(item);

                        ApiServiceImpl.addTrack(UserBehaviorAddDTO.build(item, AdEventSign.AD_REQUEST, posId));
                    });
                }
            }

            public void onError(ResponseObject rsp) {
                Log.ep(String.format("更新策略失败[%s]", adType.getId()));
            }
        });
    }

    /**
     * 缓存出列<br/>
     */
    protected <T extends BaseAd> void pop(T ad) {
        PoolMgr.getInstance().pop(ad);
    }

    /**
     * 展示广告平台控件(方便测试，对app开放)<br/>
     */
    protected void addPlatformView(AdPlatformEnum platform) {
        if (AdMergeSDKApp.isDebug()) {
            ToastUtils.showToast(context, platform.getId());
        }
    }

    /**
     * 设置展示方向<br/>
     */
    public void setOrientation(OrientationEnum orientation) {
        this.orientation = orientation;
    }

    /**
     * 获取广告事件<br/>
     */
    public T getAdListener() {
        if (this instanceof SplashAdLoader) {
            return (T) splashAdListener;
        } else if (this instanceof RewardAdLoader) {
            return (T) rewardAdListener;
        } else if (this instanceof FullScreenAdLoader) {
            return (T) fullScreenAdListener;
        } else if (this instanceof InterstitialAdLoader) {
            return (T) interstitialAdListener;
        } else if (this instanceof BannerAdLoader) {
            return (T) bannerAdListener;
        } else if (this instanceof FeedAdLoader) {
            return (T) feedAdListener;
        }
        return null;
    }

    /**
     * 更新事件订阅者<br/>
     */
    public void updateListener(T t) {
        if (this instanceof SplashAdLoader) {
            splashAdListener = (SplashAdListener) t;
        } else if (this instanceof RewardAdLoader) {
            rewardAdListener = (RewardAdListener) t;
        } else if (this instanceof FullScreenAdLoader) {
            fullScreenAdListener = (FullScreenAdListener) t;
        } else if (this instanceof InterstitialAdLoader) {
            interstitialAdListener = (InterstitialAdListener) t;
        } else if (this instanceof BannerAdLoader) {
            bannerAdListener = (BannerAdListener) t;
        } else if (this instanceof FeedAdLoader) {
            feedAdListener = (FeedAdListener) t;
        }
    }

    /**
     * 加载<br/>
     */
    protected abstract void load(AdPlaceWebConfigureVO config);

    /**
     * 展示<br/>
     */
    protected abstract void show(BaseAd ad);
}
